package plgbase

import (
	"dgo/goutils/utils"
	"fmt"
	"gitee.com/ymofen/gobase/golog"
	"gitee.com/ymofen/gobase/subpub"
	"gitee.com/ymofen/supervalue"
	"os"
	"runtime"
	"strings"
	"sync/atomic"
	"time"
)

var (
	PLUG_SHOW_CONF byte  = 1
	plg_alive_cnt  int32 = 0
)

type PlugConfRec struct {
	t            time.Time
	id, plugtype string
	confstr      string
	err          error
}

func NewPlugConfRec() *PlugConfRec {
	rval := &PlugConfRec{}
	return rval
}

/*
*
从JSON配置中创建或者重载配置,
配置可以是单个JSON的配置对象如:

	{
	  "id":"id",
	  "type":"xxxxx",
	  "conf":{...}
	}

也可以是一个数组, 例如:
[

	{"id":"id", "conf":{}}

]
*/
func ReloadPlgFromJSON(req *wrapper.SuperValue) (rval interface{}, errcode int, err error) {
	if req.IsArray() {
		req.Range(func(key string, val *wrapper.SuperValue) bool {
			_, errcode, err = PlgCheckReloadFromJSON(val)
			if err != nil {
				golog.Debugf("创建插件异常:%s, 配置:%s", err.Error(), val.AsJSONString(false))
				return false
			}

			return true
		})
	} else {
		return PlgCheckReloadFromJSON(req)
	}
	return
}

func ReloadPlgFromJSONList(req *wrapper.SuperValue) (succ int, errcode int, err error) {
	var lst *wrapper.SuperValue
	if req.IsArray() {
		lst = req
	} else {
		lst = req.ValueByName("list")
	}
	if lst == nil {
		errcode = ERR_IN_PARAM_INVALID
		err = fmt.Errorf("无法找到列表")
		return
	}

	succ = 0
	lst.Range(func(key string, val *wrapper.SuperValue) bool {
		_, errcode, err = PlgCheckReloadFromJSON(val)
		if err != nil {
			golog.Debugf("创建插件异常:%s, 配置:%s", err.Error(), val.AsJSONString(false))
			return false
		}
		succ++
		return true
	})

	return
}

func PlgCheckReloadFromJSONStr(val string) (rval interface{}, errcode int, err error) {
	json, e1 := wrapper.NewSVFromBuf([]byte(val))
	if e1 != nil {
		err = e1
		errcode = ERR_INNER
		return
	}

	return PlgCheckReloadFromJSON(json)
}

func PlgLoadCloseConf(id string) {
	pojo := utils.CheckGetObjPojoByGroup(pluggroup, id)
	if pojo != nil {
		str := fmt.Sprintf(`{"id": "%s","type": "NONE"}`, id)
		PlgCheckReloadFromJSONStr(str)
	}
}

// 0: 正常关闭
// 404: 未找到对应的插件
func PluginTryClose(id string, changedlst int8, flag int8) int {
	pojo := utils.CheckGetObjPojoByGroup(pluggroup, id)
	if pojo != nil {
		str := fmt.Sprintf(`{"id": "%s","type": "NONE", "changedlst":%d}`, id, changedlst)
		PlgCheckReloadFromJSONStr(str)
		return 0
	}
	return 404
}

func PlgLoadCloseConfEx(id string, changedlst int8) {
	pojo := utils.CheckGetObjPojoByGroup(pluggroup, id)
	if pojo != nil {
		str := fmt.Sprintf(`{"id": "%s","type": "NONE", "changedlst":%d}`, id, changedlst)
		PlgCheckReloadFromJSONStr(str)
	}
}

func GetPlgAliveObjectCnt() int32 {
	return atomic.LoadInt32(&plg_alive_cnt)
}

func PlgClose(id string) {
	PlgLoadCloseConfEx(id, 0)
	// 没有彻底移除
	//pojo := utils.CheckGetObjPojoByGroup(pluggroup, id)
	//if pojo != nil {
	//	obj := pojo.GetObject()
	//	if obj != nil {
	//		// 通知之前对象不再使用了
	//		if ok, _, _ := utils.TryObjectNotify(obj, pojo, utils.NOTIFY_TYPE_RELEASE, nil); ok { // 通知Release
	//
	//		} else {
	//			utils.TryClose(obj) // 执行关闭
	//		}
	//		PlgNotifyEx.Pub(PLUG_DISP_RELEASE, 0, obj, id, nil)
	//	}
	//}

}

func ReloadPlugsJSONStr(val string) (succ int, errcode int, err error) {
	json, e1 := wrapper.NewSVFromBuf([]byte(val))
	if e1 != nil {
		err = e1
		errcode = ERR_INNER
		succ = 0
		return
	}

	return ReloadPlgFromJSONList(json)
}

func TryFlagParse(fn func(cmd *utils.FlagSet)) {
	cmdflag := utils.NewFlagSet("flag-parse", utils.IgnoreOnError)
	fn(cmdflag)
	cmdflag.Parse(os.Args[1:])
}

var (
	OnBeforeLoadPlugFunc func(id string, plugtype string, plgconf *wrapper.SuperValue) bool
)

func PlgCheckReloadFromJSON(val *wrapper.SuperValue) (rval interface{}, errcode int, err error) {
	id := val.StringByName("id", "")
	if len(id) == 0 {
		golog.Warnf("配置[%s]忽略,没有指定id", val.String())
		return nil, 0, nil
	}
	plugtype := val.StringByName("type", "")
	if len(plugtype) == 0 {
		plugtype = val.StringByPath("conf.type", "")
	}

	fn := OnBeforeLoadPlugFunc
	if fn != nil {
		if !fn(id, plugtype, val) {

		}
	}

	golog.Debugf("[%s]加载插件, type:%s", id, plugtype)

	// time.Sleep(time.Millisecond * 100)

	plgConf := NewPlugConfRec()
	plgConf.id = id
	plgConf.t = time.Now()
	plgConf.plugtype = plugtype
	conf := val.ValueByName("conf")
	if conf != nil {
		plgConf.confstr = conf.String()
	}

	rval, errcode, err = innerPlgCheckReloadConf(plgConf, conf)
	if err != nil {
		PlgWarning.Push(fmt.Sprintf("[%s]加载插件出现异常:%s", utils.NowString(), val.String()))
	}

	if val.Int8ByName("changedlst", 1) == 1 {
		PlgChangedLst.Push(plgConf)
	}
	return
}

var (
	pluggroup     = utils.CheckGetObjGroup("plugs")
	PlgNotifyEx   = subpub.NewSubscribe()
	PlgChangedLst = utils.NewSyncCycleList()
)

func innerReloadConf(obj interface{}, conf *wrapper.SuperValue) error {
	// 重载配置
	if ok, err := wrapper.TryReloadConf(obj, conf); ok {
		if utils.ErrIsUnChangedErr(err) { // 未改变
			return nil
		} else {
			return err
		}
	} else {
		if ok, err := utils.TrySetConfig(obj, conf); ok {
			if utils.ErrIsUnChangedErr(err) { // 未改变
				return nil
			} else {
				return err
			}
		}
		_, _, err = utils.TryObjectNotify(obj, nil, utils.NOTIFY_TYPE_SETCONF, conf)
		if utils.ErrIsUnChangedErr(err) { // 未改变
			return nil
		} else {
			return err
		}
	}
}

func GetPlgChangedData(lst *wrapper.SuperValue, searchval string) int {
	j := 0
	PlgChangedLst.Range(func(args ...interface{}) bool {
		if len(args) == 0 {
			return true
		}

		if args[0] == nil {
			return true
		}

		rec := args[0].(*PlugConfRec)
		isOK := true
		confstr := rec.confstr

		if len(searchval) > 0 {
			isOK = strings.Contains(rec.id, searchval) || strings.Contains(confstr, searchval)
		}
		if !isOK {
			return true
		}

		itm := lst.AppendObject()
		itm.SetKeyValues("sn", j+1, "id", rec.id, "type", rec.plugtype, "t", utils.DateTimeString(rec.t))
		if PLUG_SHOW_CONF == 1 {
			itm.SetKeyValue("conf", confstr)
		} else {
			itm.SetKeyValue("conf", "****")
		}
		if rec.err != nil {
			itm.SetKeyValue("err", rec.err.Error())
		}
		j++
		return true
	})
	return j
}

/*
1.如果插件不存在则进行创建
2.插件重新加载配置
*/
func innerPlgCheckReloadConf(plgconf *PlugConfRec, conf *wrapper.SuperValue) (rval interface{}, errcode int, err error) {
	if utils.GoFunCatchException {
		defer utils.PanicHandler()
	}

	pojo := utils.CheckGetObjPojoByGroup(pluggroup, plgconf.id)

	err = pojo.CheckInnerObject(plgconf.plugtype,
		func(obj interface{}) { // 释放了之前的插件
			closePlugObject(pojo)
		}, func(obj interface{}) { // 创建了插件
			TrySetPlugID(obj, plgconf.id)
			utils.TryOnCreate(obj)
			atomic.AddInt32(&plg_alive_cnt, 1)
			runtime.SetFinalizer(obj, func(obj interface{}) {
				atomic.AddInt32(&plg_alive_cnt, -1)
			})

		})
	if err != nil {
		if utils.ErrIsUnChangedErr(err) { // 未改变
			err = nil
			return
		}
		plgconf.err = err
		return
	}

	plgObj := pojo.GetObject()
	if plgObj == nil { // 创建失败, 或者关闭
		// 从插件列表中移除
		pluggroup.Remove(plgconf.id)
		return
	}

	// 重载配置
	err = innerReloadConf(pojo.GetObject(), conf)

	plgconf.err = err
	if err != nil && !utils.ErrIsUnChangedErr(err) {
		errcode = ERR_INNER
		return
	} else {
		PlgNotifyEx.Pub(PLUG_DISP_CHANGED, 0, pojo.GetObject(), plgconf.id)
	}
	rval = pojo.GetObject()
	return
}

//func CmdPrepare4Plugs() {
//	flag.StringVar(&conf_file, "config-file", "", "")
//}

func CheckReloadPlugFromJSONFile() error {
	strfile := GetStartupConfigFile()

	if len(strfile) == 0 {
		if len(os.Args) == 2 {
			str := os.Args[1]
			if utils.FileIsExists(str) {
				strfile = str
			}
		}
	}
	golog.Debugf("准备加载配置文件:%s", strfile)
	time.Sleep(time.Millisecond * 100)
	if len(strfile) > 0 {
		return CheckReloadPlgFormFile(strfile)
	} else {
		return fmt.Errorf("没有指定配置文件")
	}
}

func CheckReloadPlgFromJSON(json *wrapper.SuperValue, onPlgFunc func(plg interface{}, json *wrapper.SuperValue, errcode int, err error) bool) (n int, err error) {
	if json == nil {
		return 0, nil
	}

	var lst *wrapper.SuperValue

	if json.IsArray() {
		lst = json
	} else {
		if json.Exists("type") && json.Exists("id") { // 本身是插件

		} else {
			lst = json.ValueByName("list")
			if lst != nil && !lst.IsArray() {
				lst = nil
			}
		}
	}

	if lst != nil {
		lst.Range(func(key string, val *wrapper.SuperValue) bool {
			plg, errcode, err := PlgCheckReloadFromJSON(val)
			if plg != nil {
				n++
			}
			if onPlgFunc != nil {
				if !onPlgFunc(plg, val, errcode, err) {
					return false
				}
			}
			return true
		})
		return n, nil
	} else {
		plg, errcode, err := PlgCheckReloadFromJSON(json)
		if plg != nil {
			n++
		}
		if onPlgFunc != nil {
			onPlgFunc(plg, json, errcode, err)
		}
		return n, nil
	}
}

func CheckReloadPlgFromFileEx(filename string, onPlgFunc func(plg interface{}, json *wrapper.SuperValue, errcode int, err error) bool) (n int, err error) {
	if len(filename) > 0 {
		json, err := wrapper.NewSVFromFile(filename)
		if err != nil {
			return 0, err
		}

		return CheckReloadPlgFromJSON(json, onPlgFunc)
	} else {
		return 0, fmt.Errorf("没有指定配置文件")
	}
}

func CheckReloadPlgFormFile(filename string) error {
	if len(filename) > 0 {
		json, err := wrapper.NewSVFromFile(filename)
		if err != nil {
			golog.Warnf("config file(%s) parse err:%s", conf_file, err.Error())
			return err
		}

		if json.IsArray() {
			_, _, err := ReloadPlgFromJSON(json)
			return err
		} else {
			_, _, err := ReloadPlgFromJSON(json.ValueByName("list"))
			return err
		}
	} else {
		return fmt.Errorf("没有指定配置文件")
	}
}

var (
	startupPlgs string = ""
)

func CmdLoad4Startup(cmd *utils.FlagSet) {
	cmd.StringVar(&startupPlgs, "plgs", "", "")
}

/*
**

	加载路径下的所有配置文件
*/
func StartupByConfigs(path string) int {
	n := 0
	utils.RangeFiles(path, 1, func(path string, file os.FileInfo) bool {
		if strings.HasSuffix(file.Name(), ".json") {
			strFile := utils.CombineFilePath(path, file.Name())
			n1, e1 := CheckReloadPlgFromFileEx(strFile, func(plg interface{}, json *wrapper.SuperValue, errcode int, err error) bool {
				if err != nil {
					golog.Warnf("加载[%s]创建插件出现异常:%s\n:配置:%s", strFile, err.Error(), json.String())
					return true // 继续创建
				}
				return true
			})
			if e1 != nil {
				golog.Warnf("加载[%s]配置异常:%s", strFile, e1.Error())
			} else {
				golog.Infof("加载插件:%d, 文件:%s", n1, strFile)
			}
			n += n1
		}
		return true
	}, nil)
	return n
}

/*
**

	加载路径下的所有配置文件
*/
func StartupByConfigsEx(path string, prefix string) int {
	n := 0
	utils.RangeFiles(path, 1, func(path string, file os.FileInfo) bool {
		strfile := file.Name()
		if strings.HasPrefix(strfile, prefix) && strings.HasSuffix(strfile, ".json") {
			strFile := utils.CombineFilePath(path, file.Name())
			n1, e1 := CheckReloadPlgFromFileEx(strFile, func(plg interface{}, json *wrapper.SuperValue, errcode int, err error) bool {
				if err != nil {
					golog.Infof("加载[%s]创建插件出现异常:%s\n:配置:%s", strFile, err.Error(), json.String())
					return true // 继续创建
				}
				return true
			})
			if e1 != nil {
				golog.Warnf("加载[%s]配置异常:%s", strFile, e1.Error())
			} else {
				golog.Infof("加载插件:%d, 文件:%s", n1, strFile)
			}
			n += n1
		}
		return true
	}, nil)
	return n
}

func LoadStartupLinuxMonitor() {
	str := `
  {
    "id": "sys-monitor",
    "type": "sys.linux.monitor",
    "conf": {

    }
  }
`
	if utils.IsLinux() {
		PlgCheckReloadFromJSONStr(str)
	}
}

func LoadStartupPlgs() int {
	templ := `
  {
    "id": "$plg-type$",
    "type": "$plg-type$",
    "conf": {
    }
  }
`
	LoadStartupLinuxMonitor()
	n := 0
	if len(startupPlgs) > 0 {
		plgs := strings.Split(startupPlgs, ",")
		for i := 0; i < len(plgs); i++ {
			str := strings.ReplaceAll(templ, "$plg-type$", plgs[i])
			_, _, e1 := PlgCheckReloadFromJSONStr(str)
			if e1 != nil {
				golog.Warnf("加载插件异常:%s", e1.Error())
			} else {
				n++
			}
		}
	}
	return n
}
